package simple.framework.util.image;

import java.io.File;
import javax.imageio.ImageIO;   
import java.awt.image.BufferedImage;

public class imageResize
{
  private int iWidth;
  private int iHeight;
  private int iScaleWidth;
  private int iDotsA, iDotsB; 
  private double[] iContribA, iContribB, iContribC;

  public Boolean saveImageAsJPEG(String argFromFileStr, String argSaveToFileStr, int argFormatWidth, int argFormatHeight)
  {
    Boolean tBoolean = false;
    String tFromFileStr = argFromFileStr;
    String tSaveToFileStr = argSaveToFileStr;
    int tFormatWidth = argFormatWidth;
    int tFormatHeight = argFormatHeight;
    tBoolean = saveImageAsJPEG(tFromFileStr, tSaveToFileStr, tFormatWidth, tFormatHeight, 1);
    return tBoolean;
  }

  public Boolean saveImageAsJPEG(String argFromFileStr, String argSaveToFileStr, int argFormatWidth, int argFormatHeight, int argScale)
  {
    Boolean tBoolean = false;
    try
    {
      String tFromFileStr = argFromFileStr;
      String tSaveToFileStr = argSaveToFileStr;
      int tFormatWidth = argFormatWidth;
      int tFormatHeight = argFormatHeight;
      int tScale = argScale;
      BufferedImage tBufferedImage;
      File tFromFile = new File(tFromFileStr);
      File tSaveFile = new File(tSaveToFileStr);
      tBufferedImage = ImageIO.read(tFromFile);
      int tUWidth = tFormatWidth;
      int tUHeight = tFormatHeight;
      if (tScale == 1)
      {
        int tImageWidth = tBufferedImage.getWidth(null);
        int tImageHeight = tBufferedImage.getHeight(null);
        if (tImageWidth > 0 && tImageHeight > 0)
        {
          if (tImageWidth / tImageHeight >= tFormatWidth / tFormatHeight)
          {
            if (tImageWidth > tFormatWidth)
            {
              tUWidth = tFormatWidth;   
              tUHeight = (tImageHeight * tFormatWidth) / tImageWidth;   
            }
            else
            {
              tUWidth = tImageWidth;   
              tUHeight = tImageHeight;   
            }   
          }
          else
          {
            if (tImageHeight > tFormatHeight)
            {
              tUWidth = (tImageWidth * tFormatHeight) / tImageHeight;   
              tUHeight = tFormatHeight;   
            }
            else
            {
              tUWidth = tImageWidth;   
              tUHeight = tImageHeight;   
            }
          }
        }
      }
      tBufferedImage = imageZoomOut(tBufferedImage, tUWidth, tUHeight);
      ImageIO.write(tBufferedImage, "JPEG", tSaveFile);
      tBoolean = true;
    }
    catch(Exception e) {}
    return tBoolean;
  }
  
  private BufferedImage imageZoomOut(BufferedImage argBufferImage, int argWidth, int argHeight)
  {
    int tWidth = argWidth;
    int tHeight = argHeight;
    BufferedImage tBufferedImage = argBufferImage;
    iWidth = tBufferedImage.getWidth();
    iHeight = tBufferedImage.getHeight();
    iScaleWidth = tWidth;
    callContrib();
    BufferedImage tBufferedImageOut = horizontalFiltering(tBufferedImage, tWidth);
    BufferedImage tBufferedImageFinalOut = verticalFiltering(tBufferedImageOut, tHeight);
    return tBufferedImageFinalOut;
  }

  private double lanczos(int argI, int argInWidth, int argOutWidth, double argUValue)
  {
    int tI = argI;
    int tInWidth = argInWidth;
    int tOutWidth = argOutWidth;
    double tUValue = argUValue;
    double tDoubleX;
    double tPI = (double) 3.14159265358978;
    tDoubleX = (double) tI * (double) tOutWidth / (double) tInWidth;
    tDoubleX = Math.sin(tDoubleX * tPI) / (tDoubleX * tPI) * Math.sin(tDoubleX * tPI / tUValue) / (tDoubleX * tPI / tUValue);
    return tDoubleX;
  }

  private void callContrib()
  {   
    double tUValue = 3.0;
    iDotsB = (int) ((double) iWidth * tUValue / (double) iScaleWidth);
    iDotsA = iDotsB * 2 + 1;
    try
    {   
      iContribA = new double[iDotsA];   
      iContribB = new double[iDotsA];   
      iContribC = new double[iDotsA];   
    } catch (Exception e) {}
    int tiDotsB = iDotsB;
    iContribA[tiDotsB] = 1.0;
    double tWeight = 0.0;
    for (int i = 1; i <= tiDotsB; i ++)
    {
      iContribA[tiDotsB + i] = lanczos(i, iWidth, iScaleWidth, tUValue);   
      tWeight += iContribA[tiDotsB + i];   
    }
    for (int i = tiDotsB - 1; i >= 0; i --)
    {
      iContribA[i] = iContribA[tiDotsB * 2 - i];   
    }
    tWeight = tWeight * 2 + 1.0;
    for (int i = 0; i <= tiDotsB; i ++)
    {
      iContribB[i] = iContribA[i] / tWeight;   
    }
    for (int i = tiDotsB + 1; i < iDotsA; i ++)
    {   
      iContribB[i] = iContribB[tiDotsB * 2 - i];   
    }
  }

  private void calContribB(int argStartNum, int argStopNum)
  {   
    double tWeight = 0;
    int tStartNum = argStartNum;
    int tStopNum = argStopNum;
    for (int i = tStartNum; i <= tStopNum; i ++)
    {
      tWeight += iContribA[i];   
    }
    for (int i = tStartNum; i <= tStopNum; i ++)
    {
      iContribC[i] = iContribA[i] / tWeight;   
    }
  }

  private int getRedValue(int argRGBValue)
  {
    int tRGBValue = argRGBValue;
    int tRedValue = tRGBValue & 0x00ff0000;
    tRedValue = tRedValue >> 16;
    return tRedValue;
  }

  private int getGreenValue(int argRGBValue)
  {
    int tRGBValue = argRGBValue;
    int tGreenValue = tRGBValue & 0x0000ff00;
    tGreenValue = tGreenValue >> 8;
    return tGreenValue;
  }

  private int getBlueValue(int argRGBValue)
  {
    int tRGBValue = argRGBValue;
    int tBlueValue = tRGBValue & 0x000000ff;
    return tBlueValue;
  }

  private int comRGB(int argRedValue, int argGreenValue, int argBlueValue)
  {
    int tRGB = 0;
    int tRedValue = argRedValue;
    int tGreenValue = argGreenValue;
    int tBlueValue = argBlueValue;
    tRGB = (tRedValue << 16) + (tGreenValue << 8) + tBlueValue;
    return tRGB;
  }

  private int horizontalFilter(BufferedImage argBufferedImage, int argStartX, int argStopX, int argStart, int argStop, int argY, double[] argPContrib)
  {
    int tY = argY;
    int tValueRGB = 0;
    int tStartX = argStartX;
    int tStopX = argStopX;
    int tStart = argStart;
    int tStop = argStop;
    double[] tPContrib = argPContrib;
    BufferedImage tBufferedImage = argBufferedImage;
    double tValueRed = 0.0;
    double tValueGreen = 0.0;
    double tValueBlue = 0.0;
    int i, j;
    for (i = tStartX, j = tStart; i <= tStopX; i ++, j ++)
    {
      tValueRGB = tBufferedImage.getRGB(i, tY);
      tValueRed += getRedValue(tValueRGB) * tPContrib[j];
      tValueGreen += getGreenValue(tValueRGB) * tPContrib[j];
      tValueBlue += getBlueValue(tValueRGB) * tPContrib[j];
    }
    tValueRGB = comRGB(clip((int) tValueRed), clip((int) tValueGreen), clip((int) tValueBlue));
    return tValueRGB;
  }
  
  private BufferedImage horizontalFiltering(BufferedImage argBufferedImage, int argIOutW)
  {
    int tIOutW = argIOutW;
    BufferedImage tBufferedImage = argBufferedImage;
    int tdwInW = tBufferedImage.getWidth();
    int tdwInH = tBufferedImage.getHeight();
    int tValue = 0;
    BufferedImage tBufferedImageOut = new BufferedImage(tIOutW, tdwInH, BufferedImage.TYPE_INT_RGB);

    for (int x = 0; x < tIOutW; x ++)
    {
      int tStartX;   
      int tStart;   
      int tX = (int) (((double) x) * ((double) tdwInW) / ((double) tIOutW) + 0.5);   
      int tY = 0;

      tStartX = tX - iDotsB;   
      if (tStartX < 0)
      {   
        tStartX = 0;   
        tStart = iDotsB - tX;   
      }
      else
      {
        tStart = 0;
      }
      int tStop;
      int tStopX = tX + iDotsB;   
      if (tStopX > (tdwInW - 1))
      {
        tStopX = tdwInW - 1;
        tStop = iDotsB + (tdwInW - 1 - tX);
      }
      else
      {
        tStop = iDotsB * 2;
      }

      if (tStart > 0 || tStop < iDotsA - 1)
      {
        calContribB(tStart, tStop);
        for (tY = 0; tY < tdwInH; tY ++)
        {
          tValue = horizontalFilter(tBufferedImage, tStartX, tStopX, tStart, tStop, tY, iContribC);
          tBufferedImageOut.setRGB(x, tY, tValue);
        }
      }
      else
      {
        for (tY = 0; tY < tdwInH; tY ++)
        {
          tValue = horizontalFilter(tBufferedImage, tStartX, tStopX, tStart, tStop, tY, iContribB);
          tBufferedImageOut.setRGB(x, tY, tValue);
        }
      }
    }
    return tBufferedImageOut;   
  }
  
  private int verticalFilter(BufferedImage argBufferedImage, int argStartY, int argStopY, int argStart, int argStop, int argX, double[] argPContrib)
  {
    int tX = argX;
    int tValueRGB = 0;
    int tStartY = argStartY;
    int tStopY = argStopY;
    int tStart = argStart;
    int tStop = argStop;
    double[] tPContrib = argPContrib;
    BufferedImage tBufferedImage = argBufferedImage;
    double tValueRed = 0.0;
    double tValueGreen = 0.0;
    double tValueBlue = 0.0;
    int i, j;
    for (i = tStartY, j = tStart; i <= tStopY; i ++, j ++)
    {
      tValueRGB = tBufferedImage.getRGB(tX, i);
      tValueRed += getRedValue(tValueRGB) * tPContrib[j];
      tValueGreen += getGreenValue(tValueRGB) * tPContrib[j];
      tValueBlue += getBlueValue(tValueRGB) * tPContrib[j];
    }
    tValueRGB = comRGB(clip((int) tValueRed), clip((int) tValueGreen), clip((int) tValueBlue));
    return tValueRGB;
  }

  private BufferedImage verticalFiltering(BufferedImage argBufferedImage, int argIOutH)
  {
    int tIOutH = argIOutH;
    BufferedImage tBufferedImage = argBufferedImage;
    int tIW = tBufferedImage.getWidth();
    int tIH = tBufferedImage.getHeight();
    int tValue = 0;
    BufferedImage tBufferedImageOut = new BufferedImage(tIW, tIOutH, BufferedImage.TYPE_INT_RGB);
    for (int y = 0; y < tIOutH; y ++)
    {
      int tStartY;
      int tStart;
      int tY = (int) (((double) y) * ((double) tIH) / ((double) tIOutH) + 0.5);
      tStartY = tY - iDotsB;
      if (tStartY < 0)
      {
        tStartY = 0;
        tStart = iDotsB - tY;
      }
      else
      {
        tStart = 0;
      }
      int tStop;   
      int tStopY = tY + iDotsB;
      if (tStopY > (int) (tIH - 1))
      {
        tStopY = tIH - 1;
        tStop = iDotsB + (tIH - 1 - tY);
      }
      else
      {
        tStop = iDotsB * 2;
      }
  
      if (tStart > 0 || tStop < iDotsA - 1)
      {
        calContribB(tStart, tStop);
        for (int x = 0; x < tIW; x ++)
        {
          tValue = verticalFilter(tBufferedImage, tStartY, tStopY, tStart, tStop, x, iContribC);
          tBufferedImageOut.setRGB(x, y, tValue);
        }
      }
      else
      {
        for (int x = 0; x < tIW; x ++)
        {
          tValue = verticalFilter(tBufferedImage, tStartY, tStopY, tStart, tStop, x, iContribB);
          tBufferedImageOut.setRGB(x, y, tValue);
        }
      }
    }
    return tBufferedImageOut;
  }

  private int clip(int argX)
  {
    int tX = argX;
    int tclip = 220;
    if (tX < 0) tclip = 0;
    else if (tX > 255) tclip = 255;
    else tclip = tX;
    return tclip;   
  }
}